package main

import (
	"context"
	"log/slog"
	"os"
	"strings"

	logotelbridge "go.opentelemetry.io/contrib/bridges/otelslog"
	logotelglobal "go.opentelemetry.io/otel/log/global"
)

type SlogAdapter struct {
	*slog.Logger
}

// add Write so SlogAdapter satisfies io.Writer (Echo's logger output)
func (a *SlogAdapter) Write(p []byte) (int, error) {
	msg := strings.TrimSpace(string(p))
	// Echo middleware writes request lines at INFO; use Info here.
	a.Info(msg)
	return len(p), nil
}

type tee struct {
	a, b slog.Handler
	minA slog.Level
	minB slog.Level
}

// a = stdout
// b = otel
// minA = minimum level for stdout
// minB = minimum level for otel (from OTEL_LOG_LEVEL)
func NewTee(a, b slog.Handler, minA, minB slog.Level) slog.Handler {
	return &tee{a: a, b: b, minA: minA, minB: minB}
}

func (t *tee) Enabled(ctx context.Context, level slog.Level) bool {
	if (t.minA == 0 || level >= t.minA) && t.a.Enabled(ctx, level) {
		return true
	}
	if (t.minB == 0 || level >= t.minB) && t.b.Enabled(ctx, level) {
		return true
	}
	return false
}

func (t *tee) Handle(ctx context.Context, r slog.Record) error {
	if t.minA == 0 || r.Level >= t.minA {
		if err := t.a.Handle(ctx, r); err != nil {
			return err
		}
	}
	if t.minB == 0 || r.Level >= t.minB {
		return t.b.Handle(ctx, r)
	}
	return nil
}

func (t *tee) WithAttrs(attrs []slog.Attr) slog.Handler {
	return NewTee(t.a.WithAttrs(attrs), t.b.WithAttrs(attrs), t.minA, t.minB)
}

func (t *tee) WithGroup(name string) slog.Handler {
	return NewTee(t.a.WithGroup(name), t.b.WithGroup(name), t.minA, t.minB)
}

func SetupLogger(ctx context.Context) (*SlogAdapter, func(context.Context) error, error) {
	stdout := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
			// drop the default time attribute so text output has no timestamp
			if a.Key == "time" {
				return slog.Attr{}
			}
			return a
		},
	})
	otelHandler := logotelbridge.NewHandler("slog", logotelbridge.WithLoggerProvider(logotelglobal.GetLoggerProvider()))

	minStdout := parseLogLevel(os.Getenv("STDOUT_LOG_LEVEL"))
	minOtel := parseLogLevel(os.Getenv("OTEL_LOG_LEVEL"))

	handler := NewTee(stdout, otelHandler, minStdout, minOtel)

	logger := slog.New(handler)
	adapter := &SlogAdapter{logger}
	shutdown := func(ctx context.Context) error { return nil }

	slog.SetDefault(adapter.Logger)
	return adapter, shutdown, nil
}

func parseLogLevel(v string) slog.Level {
	v = strings.ToUpper(strings.TrimSpace(v))
	if v == "" {
		return slog.LevelInfo
	}
	m := map[string]slog.Level{
		"TRACE":   slog.LevelDebug,
		"DEBUG":   slog.LevelDebug,
		"INFO":    slog.LevelInfo,
		"WARN":    slog.LevelWarn,
		"WARNING": slog.LevelWarn,
		"ERROR":   slog.LevelError,
	}
	if lv, ok := m[v]; ok {
		return lv
	}
	return slog.LevelInfo
}
